package com.ruoyi.data.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.enums.DataListStatusType;
import com.ruoyi.common.enums.OrderType;
import com.ruoyi.common.helper.LoginHelper;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.domain.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.common.utils.redis.RedisUtils;
import com.ruoyi.data.domain.DataClassification;
import com.ruoyi.data.domain.DataClassificationUser;
import com.ruoyi.data.domain.HomeColConfig;
import com.ruoyi.data.domain.bo.getData.GetDataDataListBo;
import com.ruoyi.data.mapper.DataClassificationMapper;
import com.ruoyi.data.mapper.DataClassificationUserMapper;
import com.ruoyi.data.mapper.HomeColConfigMapper;
import com.ruoyi.system.mapper.SysUserMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.*;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import com.ruoyi.data.domain.bo.DataListBo;
import com.ruoyi.data.domain.vo.DataListVo;
import com.ruoyi.data.domain.DataList;
import com.ruoyi.data.mapper.DataListMapper;
import com.ruoyi.data.service.IDataListService;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * 数据列表Service业务层处理
 *
 * @author ruoyi
 * @date 2023-10-26
 */
@RequiredArgsConstructor
@Service
@Slf4j
public class DataListServiceImpl implements IDataListService {

    private final DataListMapper baseMapper;
    private final DataClassificationMapper dataClassificationMapper;
    private final SysUserMapper sysUserMapper;
    private final DataClassificationUserMapper classificationUserMapper;
    private final HomeColConfigMapper homeColConfigMapper;

    /**
     * 查询数据列表
     */
    @Override
    public DataListVo queryById(String dataId){

        DataListVo dataListVo = baseMapper.selectVoById(dataId);

        DataClassification dataClassification = dataClassificationMapper.selectById(dataListVo.getClassificationId());
        if (dataClassification != null){
            dataListVo.setClassificationName(dataClassification.getClassificationName());
            dataListVo.setTableHead(dataClassification.getTableHead());
        }
        return dataListVo;
    }

    /**
     * 查询数据列表列表
     *
     * todo 新增传入筛选条件 字段按文本传入
     */
    @Override
    public TableDataInfo<DataListVo> queryPageList(DataListBo bo, PageQuery pageQuery) {
        QueryWrapper<DataList> lqw = buildQueryWrapper(bo);

        HomeColConfig homeColConfig = homeColConfigMapper.selectOne(new LambdaQueryWrapper<HomeColConfig>()
            .eq(HomeColConfig::getClassificationId, bo.getClassificationId())
            //.eq(HomeColConfig::getStatus, bo.getStatus())
            .eq(HomeColConfig::getUserId, LoginHelper.getUserId())
        );

        if (homeColConfig != null){
            bo.setFilterDataList(JSONUtil.toList(homeColConfig.getJson(), DataListBo.filterDataList.class));
            bo.getFilterDataList().forEach(filterDataList -> {
                assemblyFilterData(lqw,filterDataList.getField(),filterDataList.getFieldType(), filterDataList.getFilterData1(), filterDataList.getFilterData2());
            });
        }



        Page<DataListVo> result = baseMapper.selectDataListVoPage(pageQuery.build(), lqw);
        assemblyData(result.getRecords());

        return TableDataInfo.build(result);
    }

    /**
     * 根据 field传入的查询类型 filterData查询的字段组装lqw
     *
     * @param lqw
     * @param field
     * @param fieldType
     * @param filterData1
     * @param filterData2
     */
    private void assemblyFilterData(QueryWrapper<DataList> lqw, String field, String fieldType, String filterData1, String filterData2) {
        if (StringUtils.isEmpty(field) || StringUtils.isEmpty(fieldType)){
            throw new RuntimeException("field或fieldType不能为空");
        }

        switch (fieldType){
            case "=":
                lqw.eq(field,filterData1);
                break;
            case "!=":
                lqw.ne(field,filterData1);
                break;
            case "<":
                lqw.lt(field,filterData1);
                break;
            case "<=":
                lqw.le(field,filterData1);
               break;
            case ">":
                lqw.gt(field,filterData1);
                break;
            case "包含":
                lqw.like(field,filterData1);
                break;
            case "不包含":
                lqw.notLike(field,filterData1);
                break;
            case "开头是":
                lqw.likeLeft(field,filterData1);
                break;
            case "开头不是":
                lqw.notLikeLeft(field,filterData1);
                break;
            case "结尾是":
                lqw.likeRight(field,filterData1);
                break;
            case "结尾不是":
                lqw.notLikeRight(field,filterData1);
                break;
            case "是null":
                lqw.isNull(field);
                break;
            case "不是null":
                lqw.isNotNull(field);
                break;
            case "是空的":
                lqw.eq(field,"");
                break;
            case "是非空的":
                lqw.ne(field,"");
                break;
            case "介于":
                lqw.between(field,filterData1, filterData2);
                break;
            case "不介于":
                lqw.notBetween(field,filterData1, filterData2);
                break;
            case "在列表":
                lqw.in(field,filterData1.split(","));
                break;
            case "不在列表":
                lqw.notIn(field,filterData1.split(","));
                break;

        }


    }

    /**
     * 查询数据列表列表
     */
    @Override
    public List<DataListVo> queryList(DataListBo bo) {
        QueryWrapper<DataList> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    /**
     * 组装列表其他数据
     */
    private void assemblyData(List<DataListVo> dataListVoList){

        dataListVoList.forEach( dataListVo -> {
            //查询分类数据
            DataClassification dataClassification = dataClassificationMapper.selectById(dataListVo.getClassificationId());
            if (dataClassification != null){
                dataListVo.setClassificationName(dataClassification.getClassificationName());
                dataListVo.setTableHead(dataClassification.getTableHead());
            }

            SysUser sysUser = sysUserMapper.selectUserById(dataListVo.getUserId());
            if (sysUser != null){
                dataListVo.setUserName(sysUser.getUserName());
            }
        });

    }

    private QueryWrapper<DataList> buildQueryWrapper(DataListBo bo) {
        Map<String, Object> params = bo.getParams();
        QueryWrapper<DataList> lqw = Wrappers.query();
        lqw.eq(bo.getClassificationId() != null, "classification_id", bo.getClassificationId());
        lqw.eq(bo.getUserId() != null, "user_id", bo.getUserId());
        lqw.eq(StringUtils.isNotBlank(bo.getStatus()), "status", bo.getStatus());
        lqw.eq(StringUtils.isNotBlank(bo.getPid()), "pid", bo.getPid());
        lqw.eq(StringUtils.isNotBlank(bo.getCreateBy()), "create_by", bo.getCreateBy());
        lqw.eq(bo.getCreateTime() != null, "create_time", bo.getCreateTime());
        lqw.eq(StringUtils.isNotBlank(bo.getUpdateBy()), "update_by", bo.getUpdateBy());
        lqw.eq(bo.getUpdateTime() != null, "update_time", bo.getUpdateTime());
        lqw.eq(StringUtils.isNotBlank(bo.getData1()), "data1", bo.getData1());
        lqw.eq(StringUtils.isNotBlank(bo.getData2()), "data2", bo.getData2());
        lqw.eq(StringUtils.isNotBlank(bo.getData3()), "data3", bo.getData3());
        lqw.eq(StringUtils.isNotBlank(bo.getData4()), "data4", bo.getData4());
        lqw.eq(StringUtils.isNotBlank(bo.getData5()), "data5", bo.getData5());
        lqw.eq(StringUtils.isNotBlank(bo.getData6()), "data6", bo.getData6());
        lqw.eq(StringUtils.isNotBlank(bo.getData7()), "data7", bo.getData7());
        lqw.eq(StringUtils.isNotBlank(bo.getData8()), "data8", bo.getData8());
        lqw.eq(StringUtils.isNotBlank(bo.getData9()), "data9", bo.getData9());
        lqw.eq(StringUtils.isNotBlank(bo.getData10()), "data10", bo.getData10());
        lqw.eq(StringUtils.isNotBlank(bo.getData11()), "data11", bo.getData11());
        lqw.eq(StringUtils.isNotBlank(bo.getData12()), "data12", bo.getData12());
        lqw.eq(StringUtils.isNotBlank(bo.getData13()), "data13", bo.getData13());
        lqw.eq(StringUtils.isNotBlank(bo.getData14()), "data14", bo.getData14());
        lqw.eq(StringUtils.isNotBlank(bo.getData15()), "data15", bo.getData15());
        lqw.eq(StringUtils.isNotBlank(bo.getData16()), "data16", bo.getData16());
        lqw.eq(StringUtils.isNotBlank(bo.getData17()), "data17", bo.getData17());
        lqw.eq(StringUtils.isNotBlank(bo.getData18()), "data18", bo.getData18());
        lqw.eq(StringUtils.isNotBlank(bo.getData19()),"data19", bo.getData19());
        lqw.eq(StringUtils.isNotBlank(bo.getData20()), "data20", bo.getData20());
        lqw.eq(StringUtils.isNotBlank(bo.getData21()), "data21", bo.getData21());
        lqw.eq(StringUtils.isNotBlank(bo.getData22()), "data22", bo.getData22());
        lqw.eq(StringUtils.isNotBlank(bo.getData23()), "data23", bo.getData23());
        lqw.eq(StringUtils.isNotBlank(bo.getData24()), "data24", bo.getData24());
        lqw.eq(StringUtils.isNotBlank(bo.getData25()), "data25", bo.getData25());
        lqw.eq(StringUtils.isNotBlank(bo.getData26()), "data26", bo.getData26());
        lqw.eq(StringUtils.isNotBlank(bo.getData27()), "data27", bo.getData27());
        lqw.eq(StringUtils.isNotBlank(bo.getData28()), "data28", bo.getData28());
        lqw.eq(StringUtils.isNotBlank(bo.getData29()), "data29", bo.getData29());
        lqw.eq(StringUtils.isNotBlank(bo.getData30()), "data30", bo.getData30());
        lqw.orderByDesc("create_time");
        return lqw;
    }

    /**
     * 新增数据列表
     */
    @Override
    public Boolean insertByBo(DataListBo bo) {
        DataList add = BeanUtil.toBean(bo, DataList.class);

        //如果是管理员才能选择用户
        if (LoginHelper.isAdmin() && bo.getUserId() != null){
           add.setUserId(bo.getUserId());
        } else {
            add.setUserId(LoginHelper.getUserId());

        }


        validEntityBeforeSave(add);
        add.setPid(IdUtil.fastSimpleUUID());
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setDataId(add.getDataId());
        }
        return flag;
    }

    /**
     * 修改数据列表
     */
    @Override
    @Transactional
    public Boolean updateByBo(DataListBo bo) {
        DataList update = BeanUtil.toBean(bo, DataList.class);
        validEntityBeforeSave(update);

        DataList d = baseMapper.selectById(bo.getDataId());
        boolean flag = false;

        if (d != null){

            //如果是数据是未读  修改为未读
            //如果是数据是已读  修改为已读
//            if (StringUtils.equals(bo.getStatus(), d.getStatus()) ){
//                throw new ServiceException("状态一致无需修改！");
//            }

            judgeDataCache(d, bo.getStatus());

            flag = baseMapper.updateById(update) > 0;


        }
        return flag;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(DataList entity){
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 批量删除数据列表
     */
    @Override
    @Transactional
    public Boolean deleteWithValidByIds(Collection<String> ids, Boolean isValid) {

        List<DataList> dataLists = baseMapper.selectBatchIds(ids);

        List<String> delIds = dataLists.stream().map(DataList::getDataId).collect(Collectors.toList());

        if (!LoginHelper.isAdmin()){
            delIds = dataLists.stream().filter(dataList -> Objects.equals(LoginHelper.getUserId(), dataList.getUserId())).map(DataList::getDataId).collect(Collectors.toList());
        }

        boolean del = baseMapper.deleteBatchIds(delIds) > 0;

        //清空缓存
        cleatRedis(dataLists);

        return  del;
    }

    /**
     * 清理缓存
     */
    @Override
    public void cleatRedis(List<DataList> dataLists){
        RedissonClient client = RedisUtils.getClient();

        dataLists.forEach( dataList -> {
            String KEY = CacheConstants.ONE_DATA_KEY + dataList.getUserId() + ":" + dataList.getClassificationId();
            RScoredSortedSet<String> sortedSet = client.getScoredSortedSet(KEY);
            sortedSet.remove(dataList.getDataId());
        });


    }


    /**
     * 获取缓存中的一个id
     * @param bo
     * @param userId
     * @return
     * @throws Exception
     */
    private String getOneDataId(GetDataDataListBo bo, Long userId) throws Exception {
        RedissonClient client = RedisUtils.getClient();

        Long classificationId = bo.getClassificationId();
        String orderType = bo.getOrderType();

        String KEY = CacheConstants.ONE_DATA_KEY + userId + ":" + classificationId;

        RScoredSortedSet<String> sortedSet = client.getScoredSortedSet(KEY);

        String id;
        if (StringUtils.equals(OrderType.DESC.getOrderType(), orderType)) {
            id = sortedSet.pollLast();
        } else if (StringUtils.equals(OrderType.ASC.getOrderType(), orderType)){
            id = sortedSet.pollFirst();
        } else {
            int randomIndex = new Random().nextInt(sortedSet.size());

            List<String> list = (List<String>) sortedSet.valueRange(randomIndex, randomIndex);

            if (CollUtil.isEmpty(list)) {
                log.info("获取数据为空！");
                return null;
            }
            id = list.get(0);
            sortedSet.remove(id);
        }

        return String.valueOf(id);
    }

    /**
     * 统计消费id的速度
     */
    private void statisticsOneDataId(){

        //RedisUtils.incrAtomicValue();
    }


    @Override
    public DataList getOneDataList(GetDataDataListBo bo, Long userId) throws Exception {

        String oneDataId = getOneDataId(bo, userId);

        if (oneDataId == null){
            return null;
        }
        log.info("oneDataId:{}",oneDataId);
        DataList oneData = baseMapper.selectById(oneDataId);

        if (oneData == null || StringUtils.equals(oneData.getStatus(), DataListStatusType.READ.getClassificationType())){
            log.warn("id: {} 此数据已读或不存在 请管理员检查配置", oneData);
            return oneData;
        }

        oneData.setRead_token(bo.getToken());
        asyncUpdate(oneData);

        return oneData;
    }

    /**
     * 异步插入
     * @param oneData
     */
    @Async
    void asyncUpdate(DataList oneData){
        if (ObjectUtil.isNotNull(oneData)){
            DataList u = new DataList();
            u.setDataId(oneData.getDataId());
            u.setStatus("1");
            u.setRead_time(new Date());
            u.setRead_token(oneData.getRead_token());
            baseMapper.updateById(u);
        }
    }

    @Override
    public void batchInsertByBo(List<DataList> inList) {
        baseMapper.insertBatch(inList, 10000);
    }



    @Override
    @Transactional
    public boolean batchPaste(Long userId, List<String> ids, Long beforeClassificationId, Long classificationId, String status, String sourceStatus) {
        List<DataList> dataLists;
        AtomicInteger res = new AtomicInteger();

        //全量数据
        if (ids.size() == 0){
            dataLists = baseMapper.selectList(new LambdaQueryWrapper<DataList>()
                .select(DataList::getDataId,DataList::getUserId,DataList::getClassificationId)
                    .eq(StringUtils.isNotEmpty(sourceStatus), DataList::getStatus, sourceStatus)
                .eq(DataList::getUserId, userId)
                .eq(DataList::getClassificationId, beforeClassificationId));

            DataList update = new DataList();
            update.setClassificationId(classificationId);
            update.setStatus(status);

            //如果是未读则修改缓存状态为未缓存
            if (StringUtils.equals(status, DataListStatusType.NOT_READ.getClassificationType())){
                update.setCacheType(0);
            }
            res.addAndGet(baseMapper.update(update, new LambdaQueryWrapper<DataList>()
                .eq(DataList::getUserId, userId)
                .eq(StringUtils.isNotEmpty(sourceStatus), DataList::getStatus, sourceStatus)
                .eq(DataList::getClassificationId, beforeClassificationId)));
        }
        //少量数据
        else {
            dataLists = baseMapper.selectBatchIds(ids);
            dataLists = dataLists.stream().filter(dataList -> Objects.equals(userId, dataList.getUserId())).collect(Collectors.toList());

            dataLists.forEach( d ->{
                DataList update = new DataList();
                update.setDataId(d.getDataId());
                update.setClassificationId(classificationId);
                update.setStatus(status);

                //如果是未读则修改缓存状态为未缓存
                if (StringUtils.equals(status, DataListStatusType.NOT_READ.getClassificationType())){
                    update.setCacheType(0);
                }

                res.addAndGet(baseMapper.updateById(update));
            });
        }

        //如果是已读改已读则无需删除缓存
        cleatRedis(dataLists);

        return res.get() >0;
    }

    /**
     *
     * @param list
     * @param b
     * @param type
     * @return
     */
    @Override
    @Transactional
    public int removeDataListByDataClassificationId(List<Long> list, boolean b, int type) {
        RedissonClient client = RedisUtils.getClient();


        List<DataClassificationUser> dataClassificationUsers = classificationUserMapper.selectList(
            new LambdaQueryWrapper<DataClassificationUser>()
                .eq(!LoginHelper.isAdmin(), DataClassificationUser::getUserId, LoginHelper.getUserId())
                .in(DataClassificationUser::getClassificationId, list)
        );
        AtomicInteger delete = new AtomicInteger();
        dataClassificationUsers.forEach( d ->{
            delete.addAndGet(baseMapper.delete(
                new LambdaQueryWrapper<DataList>()
                    .eq(!LoginHelper.isAdmin(), DataList::getUserId, d.getUserId())
                    .eq(DataList::getClassificationId, d.getClassificationId())
                    .eq(type != 2, DataList::getStatus, type)
            ));

            String KEY = CacheConstants.ONE_DATA_KEY + d.getUserId() + ":" + d.getClassificationId();

            RScoredSortedSet<String> sortedSet = client.getScoredSortedSet(KEY);
            sortedSet.delete();
        });




        return delete.get();
    }

    @Override
    @Transactional
    public int removeDataListByDataClassificationIdByConfigJson(Long classificationId) {

       HomeColConfig homeColConfig = homeColConfigMapper.selectOne(new LambdaQueryWrapper<HomeColConfig>()
            .eq(HomeColConfig::getClassificationId, classificationId)
            .eq(HomeColConfig::getUserId, LoginHelper.getUserId())
        );

       if (homeColConfig == null) throw new RuntimeException("配置不能为空");
       if (StringUtils.isEmpty(homeColConfig.getJson())) throw new RuntimeException("配置不能为空");

       List<DataListBo.filterDataList> list = JSONUtil.toList(homeColConfig.getJson(), DataListBo.filterDataList.class);

        QueryWrapper<DataList> lqw = new QueryWrapper<>();
        list.forEach(filterDataList -> {
            assemblyFilterData(lqw,filterDataList.getField(),filterDataList.getFieldType(), filterDataList.getFilterData1(), filterDataList.getFilterData2());
        });

        lqw.select("data_id");
        lqw.eq("user_id", LoginHelper.getUserId());
        List<DataList> delIds = baseMapper.selectList(lqw);

        boolean del = baseMapper.deleteBatchIds(delIds.stream().map(DataList::getDataId).collect(Collectors.toList())) > 0;

        //清空缓存
        cleatRedis(delIds);

        return 1;
    }

    /**
     * 判断数据是否需要清理缓存
     * @param d
     * @param status
     */
    private void judgeDataCache(DataList d, String status){
        //如果是数据是未读  修改为已读
        if (StringUtils.equals(d.getStatus(),DataListStatusType.NOT_READ.getClassificationType())  && StringUtils.equals(status,DataListStatusType.READ.getClassificationType()) ){
            //如果已经缓存 则删除掉缓存中的内容 修改为已读
            d.setRead_time(new Date());
            d.setRead_token("手动操作已读");
            d.setCacheType(1);
            //清空缓存
            cleatRedis(CollUtil.newArrayList(d));

        }
        //如果是数据是已读  修改为未读
        if (StringUtils.equals(d.getStatus(),DataListStatusType.READ.getClassificationType())  && StringUtils.equals(status,DataListStatusType.NOT_READ.getClassificationType()) ){
            d.setCacheType(0);
            d.setRead_time(null);
        }
    }



    public static void main(String[] args) {

        int begin =  1;
        int end = 2;
        BigDecimal newStakeTokens = new BigDecimal(0);


        String data = HttpUtil.get("https://data.nftsniper.club/titanx-api/chartData?range=180day");

        JSONObject jsonObject = new JSONObject(data);
        if (jsonObject.getInt("code") != 0){
            System.err.println("error！");
            return;
        }

        if (begin >= end){
            System.err.println("error！");
            return;
        }

        JSONArray result = jsonObject.getJSONArray("result");

        DateTime listMinDate =  DateUtil.parse(result.getJSONObject(0).getStr("time"));
        DateTime listMaxDate =   DateUtil.parse(result.getJSONObject(result.size()-1).getStr("time"));

        System.err.println("当前数据时间范围：" +  listMinDate  + " -" + listMaxDate);

        DateTime limitBeginDate = DateUtil.offsetDay(listMaxDate, -end);
        DateTime limitEndDate = DateUtil.offsetDay(listMaxDate, -begin);

        System.err.println("筛选范围:" + limitBeginDate + "-" + limitEndDate);


        for (int i=0; i < result.size(); i++ ){
            JSONObject row = result.getJSONObject(i);
            //从小到大排列
            DateTime nowDate = DateUtil.parse(row.getStr("time"));

            //筛选出 当前日期 大于limitBeginDate  小于limitEndDate的数据
            if (DateUtil.compare(nowDate, limitBeginDate) >=0 && DateUtil.compare(nowDate, limitEndDate) <= 0)
            {
                newStakeTokens = newStakeTokens.add(BigDecimal.valueOf(row.getDouble("newStakeTokens")));
                System.err.println("原始数据：" + row);
            }
        }
        System.err.println("newStakeTokens合计：" + newStakeTokens.setScale(4, RoundingMode.HALF_UP));



    }

}
